﻿using System.Globalization;

namespace BPM.Web.Shared.Extensions;

/// <summary>
/// 异步扩展<para/>
/// netcore控制台和aspnetcore移除了SynchronizationContext，不会因为SynchronizationContext而死锁<para/>
/// 但blazor使用了RendererSynchronizationContext，要避免同步调用异步方法造成死锁
/// </summary>
internal static class AsyncExtensions
{
    /// <summary>
    /// 
    /// </summary>
    private static readonly TaskFactory s_factory = new(CancellationToken.None, 
        TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default);

    /// <summary>
    /// 基于TaskFactory执行异步调用<para/>
    /// 与Task.Run的区别是TaskFactory.StartNew可以配置撤销
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="func"></param>
    /// <returns></returns>
    public static T FactoryInvokeAndWait<T>(Func<Task<T>> func)
    {
        var cultureUi = CultureInfo.CurrentUICulture;
        var culture = CultureInfo.CurrentCulture;
        return s_factory.StartNew(() =>
        {
            Thread.CurrentThread.CurrentCulture = culture;
            Thread.CurrentThread.CurrentUICulture = cultureUi;
            return func();
        }).Unwrap().GetAwaiter().GetResult();
    }

    /// <summary>
    /// 基于TaskFactory执行异步调用
    /// 与Task.Run的区别是TaskFactory.StartNew可以配置撤销
    /// </summary>
    /// <param name="func"></param>
    public static void FactoryInvokeAndWait(Func<Task> func)
    {
        var cultureUi = CultureInfo.CurrentUICulture;
        var culture = CultureInfo.CurrentCulture;
        s_factory.StartNew(() =>
        {
            Thread.CurrentThread.CurrentCulture = culture;
            Thread.CurrentThread.CurrentUICulture = cultureUi;
            return func();
        }).Unwrap().GetAwaiter().GetResult();
    }

    /// <summary>
    /// 直接执行异步调用
    /// </summary>
    /// <param name="func"></param>
    public static void InvokeAndWait(Func<Task> func)
        => Task.Run(() => func())
            .ContinueWith(t => t.Wait())
            .Wait();

    /// <summary>
    /// 直接执行异步调用
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="func"></param>
    /// <returns></returns>
    public static T? InvokeAndWait<T>(Func<Task<T>> func)
    {
        var result = default(T);
        Task.Run(() => func())
            .ContinueWith(t =>
            {
                t.Wait();
                result = t.Result;
            }).Wait();

        return result;
    }
}
